package box2d_go

type b2RayCastInput struct {
	P1, P2      B2Vec2
	MaxFraction float32
}
type B2RayCastOutput struct {
	Normal   B2Vec2
	Fraction float32
}
type B2AABB struct {
	LowerBound B2Vec2 ///< the lower vertex
	UpperBound B2Vec2 ///< the upper vertex
}

func (aabb *B2AABB) IsValid() bool {
	d := aabb.UpperBound.Sub(&aabb.LowerBound)
	var valid bool = d.X >= 0 && d.Y >= 0
	valid = valid && aabb.LowerBound.IsValid() && aabb.UpperBound.IsValid()
	return valid
}

func (aabb *B2AABB) GetCenter() *B2Vec2 {
	return aabb.LowerBound.Add(&aabb.UpperBound).Multiply(0.5)
}

func (aabb *B2AABB) GetExtents() *B2Vec2 {
	return aabb.UpperBound.Sub(&aabb.LowerBound).Multiply(0.5)
}

func (aabb *B2AABB) GetPerimeter() float32 {
	wx := aabb.UpperBound.X - aabb.LowerBound.X
	wy := aabb.UpperBound.Y - aabb.LowerBound.Y
	return 2.0 * (wx + wy)
}

func (aabb *B2AABB) Combine(other *B2AABB) {
	aabb.LowerBound = *B2MinV2(&aabb.LowerBound, &other.LowerBound)
	aabb.UpperBound = *B2MaxV2(&aabb.UpperBound, &other.UpperBound)
}

func (this *B2AABB) Combine2(aabb1 *B2AABB, aabb2 *B2AABB) {
	this.LowerBound = *B2MinV2(&aabb1.LowerBound, &aabb2.LowerBound)
	this.UpperBound = *B2MinV2(&aabb1.UpperBound, &aabb2.UpperBound)
}

func (this *B2AABB) Contains(aabb *B2AABB) bool {
	result := true
	result = result && this.LowerBound.X <= aabb.LowerBound.X
	result = result && this.LowerBound.Y <= aabb.LowerBound.Y
	result = result && aabb.UpperBound.X <= this.UpperBound.X
	result = result && aabb.UpperBound.Y <= this.UpperBound.Y
	return result
}

func (this *B2AABB) RayCast(output *B2RayCastOutput, input *b2RayCastInput) bool {
	tmin := -b2_maxFloat
	tmax := b2_maxFloat
	p := input.P1
	d := input.P2.Sub(&input.P1)
	absD := B2AbsV2(d)
	var normal B2Vec2
	for i := 0; i < 2; i++ {
		if absD.GetIndex(i) < b2_epsilon {
			if p.GetIndex(i) < this.LowerBound.GetIndex(i) || this.UpperBound.GetIndex(i) < p.GetIndex(i) {
				return false
			}
		} else {
			inv_d := float32(1.0) / d.GetIndex(i)
			t1 := (this.LowerBound.GetIndex(i) - p.GetIndex(i)) * inv_d
			t2 := (this.UpperBound.GetIndex(i) - p.GetIndex(i)) * inv_d
			var s float32 = -1.0
			if t1 > t2 {
				B2SweepFloat32(&t1, &t2)
				s = 1.0
			}
			if t1 > float32(tmin) {
				normal.SetZero()
				normal.SetIndex(i, s)
				tmin = float64(t1)
			}
			tmax := MinFloat32(float32(tmax), float32(t2))
			if float32(tmin) > float32(tmax) {
				return false
			}
		}
	}
	if float32(tmin) < float32(0.0) || input.MaxFraction < float32(tmin) {
		return false
	}
	output.Fraction = float32(tmin)
	output.Normal = normal
	return true
}
